#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>
#include <time.h>

#define forEachChild(lastnode, curnode) for(curnode = lastnode->xmlChildrenNode; curnode != NULL; curnode = curnode->next)
#define hasGoodName(node, nodeName) (!xmlStrcmp(node->name, (const xmlChar*)nodeName))

struct QueueContent
{
    char *name;
    struct QueueContent *next;
};

typedef struct QueueContent QueueContent;

QueueContent *firstLib = NULL;

char found(QueueContent *list, char *name)
{
    QueueContent *cur = list;
    while (cur != NULL)
    {
        if (strcmp(name, cur->name) == 0)
            return 1;
        cur = cur->next;
    }
    return 0;
}

void add(QueueContent **list, char *name)
{
    QueueContent *cur = *list;
    QueueContent *new;

    new = malloc(sizeof(QueueContent));
    new->name = malloc(sizeof(char) * (strlen(name) + 1));
    new->name[0] = '\0';
    strcpy(new->name, name);
    new->next = NULL;

    if (cur == NULL)
        *list = new;
    else
    {
        while (cur->next != NULL)
            cur = cur->next;
        cur->next = new;
    }
}

void freeQueue(QueueContent *list)
{
    QueueContent *cur = list;
    QueueContent *old = NULL;
    while (cur != NULL)
    {
        if (old)
            free(old);
        printf("    {\"%s\", %sFuncs, sizeof(%sFuncs) / sizeof(*%sFuncs)},\n", cur->name, cur->name, cur->name, cur->name);
        free(cur->name);
        old = cur;
        cur = cur->next;
    }
}

int main(int argc, char *argv[])
{
    xmlDocPtr doc;
    xmlNodePtr cur, prxContent, lib, libContent, func, funcContent;
    time_t curTime = time(NULL);
    int libCount = 0;

    if (argc < 2)
        exit(1);

    doc = xmlParseFile(argv[1]);

    cur = xmlDocGetRootElement(doc);
    if (cur == NULL || xmlStrcmp(cur->name, (const xmlChar*)"PSPLIBDOC"))
    {   
        printf("File is not a valid libdoc! It doesn't have the 'PSPLIBDOC' category!\n");
        xmlFreeDoc(doc);
        return 1;
    }

    for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next)
        if (!xmlStrcmp(cur->name, (const xmlChar*)"PRXFILES"))
            break;

    if (cur == NULL)
    {   
        printf("File is not a valid libdoc! It doesn't have the 'PRXFILES' category (%p, %s, %d)!\n", cur, cur->name, (cur->type == XML_ELEMENT_NODE));
        xmlFreeDoc(doc);
        return 1;
    }

    printf("/* File auto-generated on %s */\n", asctime(gmtime(&curTime)));
    printf("#include \"nidlist.h\"\n");

    forEachChild(cur, cur)
    {
        if (hasGoodName(cur, "PRXFILE"))
        {
            forEachChild(cur, prxContent)
            {
                if (hasGoodName(prxContent, "LIBRARIES"))
                {
                    forEachChild(prxContent, lib)
                    {
                        if (hasGoodName(lib, "LIBRARY"))
                        {
                            xmlChar *libName = NULL;
                            int funcCount = 0;
                            forEachChild(lib, libContent)
                                if (hasGoodName(libContent, "NAME"))
                                    libName = xmlNodeGetContent(libContent);
                            if (found(firstLib, libName))
                                continue;
                            add(&firstLib, libName);
                            printf("NIDListFunc %sFuncs[] =\n", libName);
                            printf("{\n");
                            forEachChild(lib, libContent)
                            {
                                if (hasGoodName(libContent, "FUNCTIONS"))
                                {
                                    forEachChild(libContent, func)
                                    {
                                        if (hasGoodName(func, "FUNCTION"))
                                        {
                                            xmlChar *name, *nid;
                                            name = nid = NULL;
                                            forEachChild(func, funcContent)
                                            {
                                                if (hasGoodName(funcContent, "NAME"))
                                                    name = xmlNodeGetContent(funcContent);
                                                else if (hasGoodName(funcContent, "NID"))
                                                    nid  = xmlNodeGetContent(funcContent);
                                            }
                                            if (name && nid) {
                                                funcCount++;
                                                printf("    {\"%s\", %s},\n", name, nid);
                                            }
                                            if (name)
                                                xmlFree(name);
                                            if (nid)
                                                xmlFree(nid);
                                        }
                                    }
                                }
                            }
                            printf("};\n\n");
                            xmlFree(libName);
                            libCount++;
                        }
                    }
                }
            }
        }
    }
    printf("u32 nidListNum = %d;\n", libCount);
    printf("NIDListLib nidList[] =\n");
    printf("{\n");
    freeQueue(firstLib);
    printf("};\n\n");
    xmlFreeDoc(doc);

    return 0;
}

